/**
 * Xinfei.com Inc.
 * Copyright (c) 2004-2023 All Rights Reserved.
 */
package com.xinfei.techplayprod.biz.code;

import cn.hutool.core.util.ZipUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * 代码生成器工具
 *
 * @author Chengsheng.Li
 * @version $ ExceptionUtil, v 0.1 2023/8/28 20:39 Chengsheng.Li Exp $
 */
@Component
@Slf4j
public class CodeGenUtil {

    String rootPath = "/tmp/techplay";
    //String zipName = "techplay.zip";
    @Autowired
    ResourceLoader resourceLoader;

    /**
     * 代码生成器代码
     *
     * @param applicationName 应用名
     * @param response        http response 流
     * @return void
     */
    public String gen(String applicationName, int type, HttpServletResponse response) throws IOException {

        String templatename = "techplay" + (type == 1 ? "prod" : "core");
        String configFilePath = rootPath + "/" + templatename + ".zip";
        File configFile = new File(configFilePath);
        //获取类路径下的指定文件流
        InputStream in = this.getClass().getClassLoader().getResourceAsStream(templatename + ".zip");
        FileUtils.copyInputStreamToFile(Objects.requireNonNull(in, templatename + ".zip" + "文件找不到"), configFile);
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd");
        //文件存放路径，根路径+当天的日期
        String filePath = rootPath + "/" + dateFormat.format(date);
        File destFolder = new File(filePath);
        if (!destFolder.exists()) {
            destFolder.mkdirs();
        }
        //解压代码模板zip文件
        String name;
        try {
            name = unZipFiles(configFile, filePath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        //替换文件和目录的名字
        String path = destFolder + "/" + name;        //要遍历的路径
        File file = new File(path);        //获取其file对象
        changeName(templatename, file, applicationName, templatename);

        //将指定的文件压缩
        ZipUtil.zip(path);
        //将压缩包流返回，让浏览器自动下载
        String url = destFolder + "/" + name + ".zip";
        return url;

        // browserDownLoad(url, applicationName, response);

//        catch (Throwable throwable) {
//            throw new TechplayException(TechplayErrDtlEnum.UNKNOWN_EXCEPTION, "代码生成异常，appName=" + applicationName);
//        }
    }

    /**
     * 解压zip文件到指定目录
     *
     * @param zipFile 原始压缩文件
     * @param descDir 目标目录
     * @return void
     */
    private String unZipFiles(File zipFile, String descDir) throws IOException {

        ZipFile zip = new ZipFile(zipFile, Charset.forName("GBK"));//解决中文文件夹乱码
//        String name = zip.getName().substring(zip.getName().lastIndexOf('\\')+1, zip.getName().lastIndexOf('.'));
        String name = UUID.randomUUID().toString().substring(0, 10);
        File pathFile = new File(descDir + "/" + name);
        if (!pathFile.exists()) {
            pathFile.mkdirs();
        }

        for (Enumeration<? extends ZipEntry> entries = zip.entries(); entries.hasMoreElements(); ) {
            ZipEntry entry = (ZipEntry) entries.nextElement();
            String zipEntryName = entry.getName();
            InputStream in = zip.getInputStream(entry);
            String outPath = (descDir + "/" + name + "/" + zipEntryName).replaceAll("\\*", "/");

            // 判断路径是否存在,不存在则创建文件路径
            File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
            if (!file.exists()) {
                file.mkdirs();
            }
            // 判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
            if (new File(outPath).isDirectory()) {
                continue;
            }
            // 输出文件路径信息
//			System.out.println(outPath);

            FileOutputStream out = new FileOutputStream(outPath);
            byte[] buf1 = new byte[1024];
            int len;
            while ((len = in.read(buf1)) > 0) {
                out.write(buf1, 0, len);
            }
            in.close();
            out.close();
        }
        System.out.println("******************解压完毕********************");
        return name;
    }


    /**
     * 替换文件路径中最后一个要替换的值
     *
     * @param text            原始字符串
     * @param strToReplace    待替换字符串
     * @param replaceWithThis 新替换字符串
     * @return 替换完成新字符串
     */
    private String replaceLast(String text, String strToReplace, String replaceWithThis) {
        return text.replaceFirst("(?s)" + strToReplace + "(?!.*?" + strToReplace + ")", replaceWithThis);
    }


    /**
     * 将修改后的文件写回原来的位置
     *
     * @param list     原始文件路径
     * @param filePath 目标文件夹路径
     * @return
     */
    private void save(List<String> list, String filePath) throws IOException {
        try (FileOutputStream outputStream = new FileOutputStream(filePath);
             OutputStreamWriter streamWriter = new OutputStreamWriter(outputStream);
             BufferedWriter writer = new BufferedWriter(streamWriter)
        ) {
            for (String line : list) {
                writer.write(line + System.lineSeparator());
            }
        }
    }


    /**
     * 循环遍历文件
     *
     * @param file            文件夹
     * @param applicationName 应用名
     * @return
     */
    private void changeName(String name, File file, String applicationName, String templateName) throws IOException {
        File[] fs = file.listFiles();
        assert fs != null;
        //包名
        String pkName = applicationName.replace("-", "");
        for (File f : fs) {
            if (f.isFile()) {
                List<String> list = newFile(f, applicationName, templateName);
                save(list, String.valueOf(f));
            }
            //若是目录，则判断目录名是否要替换
            if (f.isDirectory()) {
                changeName(name, f, applicationName, templateName);
            }
            //后序遍历，进行目录名的替换
            String filePath = f.getAbsolutePath();
            if (filePath.contains(name)) {
                String newFilePath = replaceLast(filePath, name, applicationName);
                new File(filePath).renameTo(new File(newFilePath));
            }
            if (filePath.contains(name)) {
                String newFilePath = replaceLast(filePath, name, applicationName.replace("-", ""));
                new File(filePath).renameTo(new File(newFilePath));
            }
        }
    }

    /**
     * 替换文件里的内容
     *
     * @param f               目标文件
     * @param applicationName 应用名
     * @return
     */
    private List<String> newFile(File f, String applicationName, String templateName) throws IOException {
        List<String> list = new ArrayList<>();
//        String pkName = applicationName.replace("-","");
        //读取文件的内容并替换成相应的
        try (FileInputStream stream = new FileInputStream(f);
             InputStreamReader streamReader = new InputStreamReader(stream);
             BufferedReader reader = new BufferedReader(streamReader)) {
            String line = "";
            while ((line = reader.readLine()) != null) {
                // 去除开始的数字
//                if(line.contains("{pname}")){
//                    line = line.replace("{pname}", applicationName);
//                }
//                if(line.contains("{pkname}")){
//                    line = line.replace("{pkname}", pkName);
//                }
                if (line.contains(templateName)) {
                    line = line.replace(templateName, applicationName);
                }
                list.add(line);
            }
        }
        return list;
    }

    /**
     * 将压缩包从浏览器下载
     *
     * @param url             目标文件
     * @param applicationName 应用名
     * @param response        http流
     * @return 无
     */
    public void browserDownLoad(String url, String applicationName, HttpServletResponse response) throws IOException {
        File zipFile = new File(url);
        InputStream fis = new BufferedInputStream(new FileInputStream(zipFile));
        byte[] buffer = new byte[fis.available()];
        fis.read(buffer);
        fis.close();
        response.reset();
        String zipTitle = applicationName;
        String fileName = URLEncoder.encode(zipTitle, "UTF-8").replaceAll("\\+", "%20");
// 设置response的Header
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".zip");
        response.setContentType("application/x-download;charset=utf-8");
        OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
        toClient.write(buffer);
        toClient.flush();
        toClient.close();
    }


    @Scheduled(cron = "0 0 1 * * ?")
    public void callDelete() {
        String path = rootPath;
        File file = new File(path);
        deleteFile(file);
    }


    public void deleteFile(File file) {
        //判断文件不为null或文件目录存在
        if (file == null || !file.exists()) {
            log.info("文件删除失败,请检查文件路径是否正确");
            return;
        }
        //取得这个目录下的所有子文件对象
        File[] files = file.listFiles();
        //遍历该目录下的文件对象
        for (File f : files) {
            //判断子目录是否存在子目录,如果是文件则删除
            if (f.isDirectory()) {
                deleteFile(f);
            } else {
                if (!f.getName().contains("techplay")) {
                    f.delete();
                }
            }
        }
        file.delete();
    }
}



